using DG.Tweening;
using DG.Tweening.Core.Easing;
using System;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using static CGF.ScrollView;

namespace CGF
{
    public class PrizeLaba : MonoBehaviour
    {
        public enum StopMethod { OutBack, OutBounce }
        public enum State { Idle, Accel, Unform, Decel, Back }

        class Cell
        {
            public int index;
            public Component component;
            public RectTransform trans;
        }

        public bool isRunning { get; private set; }

        [Header("整体偏移")]
        public float cellOffset = 0;
        [Header("Cell之间的间隔")]
        public float cellSpace = 10;
        [Header("最大速度")]
        public float maxSpeed = 20;
        [Header("加速时长（秒）")]
        public float accelDuration = 0.5f;
        [Header("匀速时长（秒）")]
        public float uniformDuration = 2;
        [Header("额外Cell个数（控制减速距离）")]
        public int addCellCount = 5;
        [Header("减速时长（秒）")]
        public float decelDuration = 1f;
        [Header("回弹距离和时长（秒）")]
        public float backDistance = 20;
        public float backDuration = 0.3f;
        [Header("Cell预置体")]
        public RectTransform cell;
        [Header("停止方式")]
        public StopMethod stopMethod = StopMethod.OutBack;

        public RectTransform rectTransform { get { return transform as RectTransform; } }
        public Action onStartAccel, onStartUniform, onStartDecel, onBounce;
        [HideInInspector]
        public List<Component> cells = new List<Component>();
        public int cellCount { private set; get; }
        public int dataCount { private set; get; }
        public State state { private set; get; }

        private float maskHeight, cellHeight;
        private List<Cell> _cells = new List<Cell>();
        private Action<Cell, int, bool> onCellUpdate;
        private Action<Cell, int> onStop;
        private DG.Tweening.Sequence quence;
        private Tween tween;
        private int topCellIndex, stopIndex;
        private float preY, moveDelta, decelDis;
        private bool canListenDis, keepUniformMove, useUpdateMethod;

        private void Update()
        {
            if (isRunning && useUpdateMethod)
            {
                UpdatePos();
                if (canListenDis)
                {
                    //当目标Cell出现在Mask视野中时，立即计算到中心（Bounce模式到回弹位置）的总距离
                    for (int i = 0; i < cellCount; i++)
                    {
                        if (_cells[i].index == stopIndex && _cells[i].trans.anchoredPosition.y > 0)
                        {
                            decelDis = _cells[i].trans.anchoredPosition.y + backDistance;
                            state = State.Decel;
                            if (stopMethod == StopMethod.OutBack)
                            {
                                StartDecel();
                            }
                            else if (stopMethod == StopMethod.OutBounce)
                            {
                                keepUniformMove = true;
                            }
                            canListenDis = false;
                            break;
                        }
                    }
                }
                if (keepUniformMove)
                {
                    decelDis += moveDelta;
                    if (decelDis <= 0)
                    {
                        decelDis = 0;
                        StartBounceBack();
                        keepUniformMove = false;
                    }
                }
            }
        }

        private Cell GetTopCell()
        {
            Cell topCell = _cells[0];
            float maxY = topCell.trans.anchoredPosition.y;
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell cell = _cells[i];
                if (cell.trans.anchoredPosition.y > maxY)
                {
                    maxY = cell.trans.anchoredPosition.y;
                    topCell = cell;
                }
            }
            return topCell;
        }

        private Cell GetBottomCell()
        {
            Cell bottomCell = _cells[0];
            float minY = bottomCell.trans.anchoredPosition.y;
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell cell = _cells[i];
                if (cell.trans.anchoredPosition.y < minY)
                {
                    minY = cell.trans.anchoredPosition.y;
                    bottomCell = cell;
                }
            }
            return bottomCell;
        }

        private Cell GetCellByIndex(int index)
        {
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell cell = _cells[i];
                float y = cell.trans.anchoredPosition.y;
                float top = maskHeight * 0.5f;
                float bottom = -maskHeight * 0.5f;
                if (cell.index == index && y < top && y > bottom)
                {
                    return cell;
                }
            }
            return null;
        }

        /// <summary>
        /// onCellUpdate: Action(T cell, int index, bool isInit)
        /// </summary>
        /// <typeparam name="T">支持绑定在Cell上已赋值脚本，避免每次都Find</typeparam>
        /// <param name="dataCount">数据总长度</param>
        /// <param name="onCellUpdate">每个Cell即将露头时触发，用于刷新Cell复用后显示的新内容</param>

        public void Init<T>(int dataCount, Action<T, int, bool> onCellUpdate) where T : Component
        {
            this.dataCount = dataCount;
            this.onCellUpdate = (Cell cell, int index, bool isInit) =>
            {
                onCellUpdate?.Invoke(cell.component as T, index, isInit);
            };

            for (int i = 0; i < this._cells.Count; i++)
            {
                if (this._cells[i].trans && this._cells[i].trans != this.cell)
                {
                    GameObject.Destroy(this._cells[i].trans.gameObject);
                }
            }
            this._cells.Clear();

            this.maskHeight = (cell.parent as RectTransform).rect.height;
            this.cellHeight = cell.rect.height;
            cellCount = Mathf.CeilToInt(maskHeight / (this.cellHeight + this.cellSpace)) + 1 + addCellCount;
            for (int i = 0; i < cellCount; i++)
            {
                RectTransform trans = null;
                if (i == 0)
                {
                    trans = this.cell;
                }
                else
                {
                    trans = GameObject.Instantiate(this.cell, this.cell.parent, false);
                }
                trans.gameObject.SetActive(true);
                Cell cell = new Cell();
                cell.trans = trans;
                cell.trans.name = $"Cell_{i}";
                this._cells.Add(cell);
            }

            cells.Clear();
            for (int i = 0; i < _cells.Count; i++)
            {
                int index = i;
                Cell _cell = _cells[index];
                _cell.component = _cell.trans.GetComponent<T>();
                _cell.index = index;
                float y = -maskHeight * 0.5f + cellSpace + cellHeight * 0.5f + i * (cellHeight + cellSpace);
                _cell.trans.anchoredPosition = new Vector2(0, y + cellOffset);
                while (_cell.index > dataCount - 1)
                {
                    _cell.index = _cell.index - dataCount;
                }
                this.onCellUpdate?.Invoke(_cell, _cell.index, true);
                cells.Add(_cell.component);
            }
        }

        /// <summary>
        /// onStop: Action(T cell, int index)
        /// </summary>
        /// <typeparam name="T">同初始化时的T</typeparam>
        /// <param name="stopIndex">希望停在哪个索引</param>
        /// <param name="onStop">停止时回调</param>
        public void StartRun<T>(int stopIndex, Action<T, int> onStop) where T : Component
        {
            if (stopIndex < 0 || stopIndex > dataCount - 1)
            {
                Debug.LogError("stopIndex超出有效范围");
                return;
            }

            if (isRunning)
            {
                return;
            }

            this.stopIndex = stopIndex;

            this.onStop = (Cell cell, int index) =>
            {
                onStop?.Invoke(cell.component as T, index);
            };

            onStartAccel?.Invoke();

            moveDelta = 0;
            int topCellIndex = GetTopCell().index;
            isRunning = true;
            useUpdateMethod = true;
            keepUniformMove = false;
            canListenDis = false;
            state = State.Accel;

            quence = DOTween.Sequence();
            //加速
            quence.Append(DOVirtual.Float(0, -maxSpeed, accelDuration, (float v) =>
              {
                  if (this.IsDestroyed()) return;
                  moveDelta = v;
              }).SetEase(Ease.InSine)).OnComplete(() =>
              {
                  onStartUniform?.Invoke();
                  state = State.Unform;
              });

            //匀速
            quence.AppendInterval(uniformDuration);

            //停止Update
            quence.AppendCallback(() =>
            {
                canListenDis = true;
            });

            //启动
            quence.Play();
        }

        public void Stop(bool useAnim, bool emitStopEvent = true)
        {
            Stop(stopIndex, useAnim, emitStopEvent);
        }

        public void Stop(int stopIndex, bool useAnim, bool emitStopEvent = true)
        {
            if (stopIndex == this.stopIndex && useAnim && (state == State.Idle || state == State.Decel || state == State.Back))
            {
                //如果目标一致且当前已经进入减速阶段，则无需再主动停止了
                return;
            }
            quence?.Kill();
            tween?.Kill();
            useUpdateMethod = false;
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell _cell = _cells[i];
                _cell.index = stopIndex - 1 + i;
                float y = -maskHeight * 0.5f + cellSpace + cellHeight * 0.5f + i * (cellHeight + cellSpace);
                if (useAnim && isRunning)
                {
                    _cell.trans.anchoredPosition = new Vector2(0, y + cellOffset - backDistance);
                    _cell.trans.DOAnchorPosY(y + cellOffset, 0.2f).SetEase(Ease.OutBack);
                }
                else
                {
                    _cell.trans.anchoredPosition = new Vector2(0, y + cellOffset);
                }
                while (_cell.index > dataCount - 1)
                {
                    _cell.index = _cell.index - dataCount;
                }
                onCellUpdate?.Invoke(_cell, _cell.index, true);
                if (emitStopEvent && _cell.index == stopIndex)
                {
                    onStop?.Invoke(_cell, _cell.index);
                }
            }
            isRunning = false;
        }

        /// <summary>
        /// 重新全部触发一次onCellUpdate()事件
        /// </summary>
        public void Refresh()
        {
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell _cell = _cells[i];
                this.onCellUpdate?.Invoke(_cell, _cell.index, false);
            }
        }

        /// <summary>
        /// 根据索引获取当前Cell
        /// </summary>
        public T GetCellByIndex<T>(int index) where T : class
        {
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell cell = _cells[i];
                if (cell.index == index)
                {
                    return cell.component as T;
                }
            }
            return default;
        }

        private void StartDecel()
        {
            useUpdateMethod = false;
            onStartDecel?.Invoke();
            preY = decelDis;
            //减速
            tween = DOVirtual.Float(decelDis, -backDistance, decelDuration, (float y) =>
            {
                if (this.IsDestroyed()) return;
                moveDelta = y - preY;
                preY = y;
                UpdatePos();
            }).SetEase(Ease.OutSine).OnComplete(() =>
            {
                //回弹
                if (this.IsDestroyed()) return;
                state = State.Back;
                DOVirtual.Float(-backDistance, 0, backDuration, (y) =>
                {
                    if (this.IsDestroyed()) return;
                    moveDelta = y - preY;
                    preY = y;
                    UpdatePos();
                }).SetEase(Ease.OutBack).OnComplete(() =>
                {
                    //结束
                    Debug.Log("结束");
                    isRunning = false;
                    state = State.Idle;
                    if (this.IsDestroyed()) return;
                    onStop?.Invoke(_cells[stopIndex], stopIndex);
                });
            });
        }

        private void StartBounceBack()
        {
            useUpdateMethod = false;
            onBounce?.Invoke();
            float targetCellY = GetCellByIndex(stopIndex).trans.anchoredPosition.y;
            float preY = targetCellY;
            state = State.Back;
            //回弹
            tween = DOVirtual.Float(targetCellY, 0, backDuration, (float y) =>
            {
                if (this.IsDestroyed()) return;
                moveDelta = y - preY;
                preY = y;
                UpdatePos();
            }).SetEase(Ease.OutBack).OnComplete(() =>
            {
                //结束
                isRunning = false;
                state = State.Idle;
                if (this.IsDestroyed()) return;
                onStop?.Invoke(_cells[stopIndex], stopIndex);
            });
        }

        private void UpdatePos()
        {
            for (int i = 0; i < _cells.Count; i++)
            {
                Cell cell = _cells[i];
                Vector2 pos = cell.trans.anchoredPosition;
                pos.y += moveDelta;
                if (pos.y + cellHeight * 0.5f < -maskHeight * 0.5f)
                {
                    pos.y += (cellHeight + cellSpace) * cellCount;
                    topCellIndex++;
                    if (topCellIndex > dataCount - 1)
                    {
                        topCellIndex = 0;
                    }
                    cell.index = topCellIndex;
                    this.onCellUpdate?.Invoke(cell, cell.index, false);
                }
                cell.trans.anchoredPosition = pos;
            }
        }

        public void OnDestroy()
        {
            quence?.Kill();
            tween?.Kill();
        }
    }
}
